﻿using System.Data;
using Microsoft.Extensions.Logging;

namespace Known.FreeSql;

class FreeSqlDatabase : Database
{
    private readonly ILoggerFactory loggerFactory;
    internal readonly IFreeSql free;

    public FreeSqlDatabase(ILoggerFactory loggerFactory) : base(loggerFactory)
    {
        this.loggerFactory = loggerFactory;
        free = FreeSqlHelper.CreateFreeSql();
        DatabaseType = FreeSqlHelper.GetDatabaseType(free.Ado.DataType);
        ConnectionString = free.Ado.ConnectionString;
    }

    public override void SetDatabase(string connName)
    {
    }

    public override Task OpenAsync()
    {
        return Task.CompletedTask;
    }

    public override Task CloseAsync()
    {
        return Task.CompletedTask;
    }

    public override Task BeginTransAsync()
    {
        return Task.CompletedTask;
    }

    public override Task CommitTransAsync()
    {
        return Task.CompletedTask;
    }

    public override Task RollbackTransAsync()
    {
        return Task.CompletedTask;
    }

    public override IQueryBuilder<T> Query<T>() => new FreeSqlQueryBuilder<T>(this);

    public override Task<PagingResult<T>> QueryPageAsync<T>(string sql, PagingCriteria criteria, Func<T, ExportColumnInfo, object> onExport = null)
    {
        throw new NotImplementedException();
    }

    public override Task<PagingResult<T>> QueryPageAsync<T>(PagingCriteria criteria, Func<T, ExportColumnInfo, object> onExport = null)
    {
        throw new NotImplementedException();
    }

    public override Task<T> QueryAsync<T>(string sql, object param = null)
    {
        return free.Ado.QuerySingleAsync<T>(sql, param);
    }

    public override Task<T> QueryAsync<T>(Expression<Func<T, bool>> expression)
    {
        return free.Select<T>().Where(expression).ToOneAsync();
    }

    public override Task<List<T>> QueryListAsync<T>()
    {
        return free.Select<T>().ToListAsync();
    }

    public override Task<List<T>> QueryListAsync<T>(string sql, object param = null)
    {
        return free.Ado.QueryAsync<T>(sql, param);
    }

    public override Task<List<T>> QueryListAsync<T>(Expression<Func<T, bool>> expression)
    {
        return free.Select<T>().Where(expression).ToListAsync();
    }

    public override Task<DataTable> QueryTableAsync(string sql, object param = null)
    {
        return free.Ado.ExecuteDataTableAsync(sql, param);
    }

    public override Task<int> ExecuteAsync(string sql, object param = null)
    {
        return free.Ado.ExecuteNonQueryAsync(sql, param);
    }

    public override async Task<T> ScalarAsync<T>(string sql, object param = null)
    {
        var scalar = await free.Ado.ExecuteScalarAsync(sql, param);
        return Utils.ConvertTo<T>(scalar);
    }

    public override async Task<List<T>> ScalarsAsync<T>(string sql, object param = null)
    {
        var data = new List<T>();
        var table = await free.Ado.ExecuteDataTableAsync(sql, param);
        if (table != null && table.Rows.Count > 0)
        {
            foreach (DataRow item in table.Rows)
            {
                var value = Utils.ConvertTo<T>(item[0]);
                data.Add(value);
            }
        }
        return data;
    }

    public override async Task<int> CountAsync<T>()
    {
        var count = await free.Select<T>().CountAsync();
        return (int)count;
    }

    public override async Task<int> CountAsync<T>(Expression<Func<T, bool>> expression)
    {
        var count = await free.Select<T>().Where(expression).CountAsync();
        return (int)count;
    }

    public override Task<int> DeleteAsync<T>(Expression<Func<T, bool>> expression)
    {
        return free.Delete<T>().Where(expression).ExecuteAffrowsAsync();
    }

    public override Task<int> DeleteAllAsync<T>()
    {
        return free.Delete<T>().ExecuteAffrowsAsync();
    }

    public override Task<int> InsertAsync<T>(T data)
    {
        return free.Insert(data).ExecuteAffrowsAsync();
    }

    public override Task<int> InsertListAsync<T>(List<T> datas)
    {
        return free.Insert(datas).ExecuteAffrowsAsync();
    }

    public override Task<int> InsertTableAsync(DataTable data)
    {
        throw new NotImplementedException();
    }

    public override Task<PagingResult<Dictionary<string, object>>> QueryPageAsync(string tableName, PagingCriteria criteria)
    {
        throw new NotImplementedException();
    }

    public override Task<bool> ExistsAsync(string tableName, string id)
    {
        throw new NotImplementedException();
    }

    public override Task<int> DeleteAsync(string tableName, string id)
    {
        var lists = new List<Dictionary<string, object>>
        {
            new Dictionary<string, object> { { nameof(EntityBase.Id), id } }
        };
        return free.Delete<object>().AsTable(tableName).Where(lists).ExecuteAffrowsAsync();
    }

    public override Task<int> InsertAsync(string tableName, Dictionary<string, object> data)
    {
        return free.Insert(data).AsTable(tableName).ExecuteAffrowsAsync();
    }

    public override Task<int> UpdateAsync(string tableName, string keyField, Dictionary<string, object> data)
    {
        throw new NotImplementedException();
    }

    protected override Task<int> SaveDataAsync<T>(T entity)
    {
        if (entity.IsNew)
            return free.Insert(entity).ExecuteAffrowsAsync();
        else
            return free.Update<T>().SetSource(entity).ExecuteAffrowsAsync();
    }

    protected override Database CreateDatabase()
    {
        var db = new FreeSqlDatabase(loggerFactory);
        db.Context = Context;
        db.User = User;
        return db;
    }

    protected override void Dispose(bool isDisposing)
    {
        base.Dispose(isDisposing);
        free.Dispose();
    }
}